#pragma once

#include <chrono>
#include <cstdint>
#include <functional>
#include <list>
#include <memory>
#include <string>
#include <vector>

#include "envoy/api/v2/core/base.pb.h"
#include "envoy/common/callback.h"
#include "envoy/config/typed_metadata.h"
#include "envoy/http/codec.h"
#include "envoy/network/connection.h"
#include "envoy/network/transport_socket.h"
#include "envoy/ssl/context.h"
#include "envoy/stats/scope.h"
#include "envoy/stats/stats.h"
#include "envoy/upstream/health_check_host_monitor.h"
#include "envoy/upstream/load_balancer_type.h"
#include "envoy/upstream/locality.h"
#include "envoy/upstream/outlier_detection.h"
#include "envoy/upstream/resource_manager.h"

#include "absl/types/optional.h"

namespace Envoy {
namespace Upstream {

/**
 * An upstream host.
 */
class Host : virtual public HostDescription {
public:
  struct CreateConnectionData {
    Network::ClientConnectionPtr connection_;
    HostDescriptionConstSharedPtr host_description_;
  };

  enum class HealthFlag {
    // The host is currently failing active health checks.
    FAILED_ACTIVE_HC = 0x1,
    // The host is currently considered an outlier and has been ejected.
    FAILED_OUTLIER_CHECK = 0x02,
    // The host is currently marked as unhealthy by EDS.
    FAILED_EDS_HEALTH = 0x04,
  };

  enum class ActiveHealthFailureType {
    // The failure type is unknown, all hosts' failure types are initialized as UNKNOWN
    UNKNOWN,
    // The host is actively responding it's unhealthy
    UNHEALTHY,
    // The host is timing out
    TIMEOUT,
  };

  /**
   * @return host specific counters.
   */
  virtual std::vector<Stats::CounterSharedPtr> counters() const PURE;

  /**
   * Create a connection for this host.
   * @param dispatcher supplies the owning dispatcher.
   * @param options supplies the socket options that will be set on the new connection.
   * @return the connection data which includes the raw network connection as well as the *real*
   *         host that backs it. The reason why a 2nd host is returned is that some hosts are
   *         logical and wrap multiple real network destinations. In this case, a different host
   *         will be returned along with the connection vs. the host the method was called on.
   *         If it matters, callers should not assume that the returned host will be the same.
   */
  virtual CreateConnectionData
  createConnection(Event::Dispatcher& dispatcher,
                   const Network::ConnectionSocket::OptionsSharedPtr& options,
                   Network::TransportSocketOptionsSharedPtr transport_socket_options) const PURE;

  /**
   * Create a health check connection for this host.
   * @param dispatcher supplies the owning dispatcher.
   * @return the connection data.
   */
  virtual CreateConnectionData
  createHealthCheckConnection(Event::Dispatcher& dispatcher) const PURE;

  /**
   * @return host specific gauges.
   */
  virtual std::vector<Stats::GaugeSharedPtr> gauges() const PURE;

  /**
   * Atomically clear a health flag for a host. Flags are specified in HealthFlags.
   */
  virtual void healthFlagClear(HealthFlag flag) PURE;

  /**
   * Atomically get whether a health flag is set for a host. Flags are specified in HealthFlags.
   */
  virtual bool healthFlagGet(HealthFlag flag) const PURE;

  /**
   * Atomically set a health flag for a host. Flags are specified in HealthFlags.
   */
  virtual void healthFlagSet(HealthFlag flag) PURE;

  /**
   * @return whether in aggregate a host is healthy and routable. Multiple health flags and other
   *         information may be considered.
   */
  virtual bool healthy() const PURE;

  /**
   * Returns the host's ActiveHealthFailureType. Types are specified in ActiveHealthFailureType.
   */
  virtual ActiveHealthFailureType getActiveHealthFailureType() const PURE;

  /**
   * Set the most recent health failure type for a host. Types are specified in
   * ActiveHealthFailureType.
   */
  virtual void setActiveHealthFailureType(ActiveHealthFailureType flag) PURE;

  /**
   * Set the host's health checker monitor. Monitors are assumed to be thread safe, however
   * a new monitor must be installed before the host is used across threads. Thus,
   * this routine should only be called on the main thread before the host is used across threads.
   */
  virtual void setHealthChecker(HealthCheckHostMonitorPtr&& health_checker) PURE;

  /**
   * Set the host's outlier detector monitor. Outlier detector monitors are assumed to be thread
   * safe, however a new outlier detector monitor must be installed before the host is used across
   * threads. Thus, this routine should only be called on the main thread before the host is used
   * across threads.
   */
  virtual void setOutlierDetector(Outlier::DetectorHostMonitorPtr&& outlier_detector) PURE;

  /**
   * @return the current load balancing weight of the host, in the range 1-100.
   */
  virtual uint32_t weight() const PURE;

  /**
   * Set the current load balancing weight of the host, in the range 1-100.
   */
  virtual void weight(uint32_t new_weight) PURE;

  /**
   * @return the current boolean value of host being in use.
   */
  virtual bool used() const PURE;

  /**
   * @param new_used supplies the new value of host being in use to be stored.
   */
  virtual void used(bool new_used) PURE;
};

typedef std::shared_ptr<const Host> HostConstSharedPtr;

typedef std::vector<HostSharedPtr> HostVector;
typedef std::unordered_map<std::string, Upstream::HostSharedPtr> HostMap;
typedef std::shared_ptr<HostVector> HostVectorSharedPtr;
typedef std::shared_ptr<const HostVector> HostVectorConstSharedPtr;

typedef std::unique_ptr<HostVector> HostListPtr;
typedef std::unordered_map<envoy::api::v2::core::Locality, uint32_t, LocalityHash, LocalityEqualTo>
    LocalityWeightsMap;
typedef std::vector<std::pair<HostListPtr, LocalityWeightsMap>> PriorityState;

/**
 * Bucket hosts by locality.
 */
class HostsPerLocality {
public:
  virtual ~HostsPerLocality() {}

  /**
   * @return bool is local locality one of the locality buckets? If so, the
   *         local locality will be the first in the get() vector.
   */
  virtual bool hasLocalLocality() const PURE;

  /**
   * @return const std::vector<HostVector>& list of hosts organized per
   *         locality. The local locality is the first entry if
   *         hasLocalLocality() is true.
   */
  virtual const std::vector<HostVector>& get() const PURE;

  /**
   * Clone object with a filter predicate.
   * @param predicate on Host entries.
   * @return HostsPerLocalityConstSharedPtr clone of the HostsPerLocality with only
   *         hosts according to predicate.
   */
  virtual std::shared_ptr<const HostsPerLocality>
  filter(std::function<bool(const Host&)> predicate) const PURE;

  /**
   * Clone object.
   * @return HostsPerLocalityConstSharedPtr clone of the HostsPerLocality.
   */
  std::shared_ptr<const HostsPerLocality> clone() const {
    return filter([](const Host&) { return true; });
  }
};

typedef std::shared_ptr<HostsPerLocality> HostsPerLocalitySharedPtr;
typedef std::shared_ptr<const HostsPerLocality> HostsPerLocalityConstSharedPtr;

// Weight for each locality index in HostsPerLocality.
typedef std::vector<uint32_t> LocalityWeights;
typedef std::shared_ptr<LocalityWeights> LocalityWeightsSharedPtr;
typedef std::shared_ptr<const LocalityWeights> LocalityWeightsConstSharedPtr;

/**
 * Base host set interface. This contains all of the endpoints for a given LocalityLbEndpoints
 * priority level.
 */
class HostSet {
public:
  virtual ~HostSet() {}

  /**
   * @return all hosts that make up the set at the current time.
   */
  virtual const HostVector& hosts() const PURE;

  /**
   * @return all healthy hosts contained in the set at the current time. NOTE: This set is
   *         eventually consistent. There is a time window where a host in this set may become
   *         unhealthy and calling healthy() on it will return false. Code should be written to
   *         deal with this case if it matters.
   */
  virtual const HostVector& healthyHosts() const PURE;

  /**
   * @return hosts per locality.
   */
  virtual const HostsPerLocality& hostsPerLocality() const PURE;

  /**
   * @return same as hostsPerLocality but only contains healthy hosts.
   */
  virtual const HostsPerLocality& healthyHostsPerLocality() const PURE;

  /**
   * @return weights for each locality in the host set.
   */
  virtual LocalityWeightsConstSharedPtr localityWeights() const PURE;

  /**
   * @return next locality index to route to if performing locality weighted balancing.
   */
  virtual absl::optional<uint32_t> chooseLocality() PURE;

  /**
   * Updates the hosts in a given host set.
   *
   * @param hosts supplies the (usually new) list of hosts in the host set.
   * @param healthy hosts supplies the subset of hosts which are healthy.
   * @param hosts_per_locality supplies the hosts subdivided by locality.
   * @param hosts_per_locality supplies the healthy hosts subdivided by locality.
   * @param locality_weights supplies a map from locality to associated weight.
   * @param hosts_added supplies the hosts added since the last update.
   * @param hosts_removed supplies the hosts removed since the last update.
   * @param overprovisioning_factor if presents, overwrites the current overprovisioning_factor.
   */
  virtual void updateHosts(HostVectorConstSharedPtr hosts, HostVectorConstSharedPtr healthy_hosts,
                           HostsPerLocalityConstSharedPtr hosts_per_locality,
                           HostsPerLocalityConstSharedPtr healthy_hosts_per_locality,
                           LocalityWeightsConstSharedPtr locality_weights,
                           const HostVector& hosts_added, const HostVector& hosts_removed,
                           absl::optional<uint32_t> overprovisioning_factor) PURE;

  /**
   * @return uint32_t the priority of this host set.
   */
  virtual uint32_t priority() const PURE;

  /**
   * @return uint32_t the overprovisioning factor of this host set.
   */
  virtual uint32_t overprovisioning_factor() const PURE;
};

typedef std::unique_ptr<HostSet> HostSetPtr;

/**
 * This class contains all of the HostSets for a given cluster grouped by priority, for
 * ease of load balancing.
 */
class PrioritySet {
public:
  typedef std::function<void(uint32_t priority, const HostVector& hosts_added,
                             const HostVector& hosts_removed)>
      MemberUpdateCb;

  virtual ~PrioritySet() {}

  /**
   * Install a callback that will be invoked when any of the HostSets in the PrioritySet changes.
   * This includes when a new HostSet is created.
   *
   * @param callback supplies the callback to invoke.
   * @return Common::CallbackHandle* a handle which can be used to unregister the callback.
   */
  virtual Common::CallbackHandle* addMemberUpdateCb(MemberUpdateCb callback) const PURE;

  /**
   * Returns the host sets for this priority set, ordered by priority.
   * The first element in the vector is the host set for priority 0, and so on.
   *
   * @return std::vector<HostSetPtr>& the host sets for this priority set.
   */
  virtual std::vector<HostSetPtr>& hostSetsPerPriority() PURE;

  /**
   * @return const std::vector<HostSetPtr>& the host sets, ordered by priority.
   */
  virtual const std::vector<HostSetPtr>& hostSetsPerPriority() const PURE;
};

/**
 * All cluster stats. @see stats_macros.h
 */
// clang-format off
#define ALL_CLUSTER_STATS(COUNTER, GAUGE, HISTOGRAM)                                               \
  COUNTER  (lb_healthy_panic)                                                                      \
  COUNTER  (lb_local_cluster_not_ok)                                                               \
  COUNTER  (lb_recalculate_zone_structures)                                                        \
  COUNTER  (lb_zone_cluster_too_small)                                                             \
  COUNTER  (lb_zone_no_capacity_left)                                                              \
  COUNTER  (lb_zone_number_differs)                                                                \
  COUNTER  (lb_zone_routing_all_directly)                                                          \
  COUNTER  (lb_zone_routing_sampled)                                                               \
  COUNTER  (lb_zone_routing_cross_zone)                                                            \
  GAUGE    (lb_subsets_active)                                                                     \
  COUNTER  (lb_subsets_created)                                                                    \
  COUNTER  (lb_subsets_removed)                                                                    \
  COUNTER  (lb_subsets_selected)                                                                   \
  COUNTER  (lb_subsets_fallback)                                                                   \
  COUNTER  (original_dst_host_invalid)                                                             \
  COUNTER  (upstream_cx_total)                                                                     \
  GAUGE    (upstream_cx_active)                                                                    \
  COUNTER  (upstream_cx_http1_total)                                                               \
  COUNTER  (upstream_cx_http2_total)                                                               \
  COUNTER  (upstream_cx_connect_fail)                                                              \
  COUNTER  (upstream_cx_connect_timeout)                                                           \
  COUNTER  (upstream_cx_idle_timeout)                                                              \
  COUNTER  (upstream_cx_connect_attempts_exceeded)                                                 \
  COUNTER  (upstream_cx_overflow)                                                                  \
  HISTOGRAM(upstream_cx_connect_ms)                                                                \
  HISTOGRAM(upstream_cx_length_ms)                                                                 \
  COUNTER  (upstream_cx_destroy)                                                                   \
  COUNTER  (upstream_cx_destroy_local)                                                             \
  COUNTER  (upstream_cx_destroy_remote)                                                            \
  COUNTER  (upstream_cx_destroy_with_active_rq)                                                    \
  COUNTER  (upstream_cx_destroy_local_with_active_rq)                                              \
  COUNTER  (upstream_cx_destroy_remote_with_active_rq)                                             \
  COUNTER  (upstream_cx_close_notify)                                                              \
  COUNTER  (upstream_cx_rx_bytes_total)                                                            \
  GAUGE    (upstream_cx_rx_bytes_buffered)                                                         \
  COUNTER  (upstream_cx_tx_bytes_total)                                                            \
  GAUGE    (upstream_cx_tx_bytes_buffered)                                                         \
  COUNTER  (upstream_cx_protocol_error)                                                            \
  COUNTER  (upstream_cx_max_requests)                                                              \
  COUNTER  (upstream_cx_none_healthy)                                                              \
  COUNTER  (upstream_rq_total)                                                                     \
  GAUGE    (upstream_rq_active)                                                                    \
  COUNTER  (upstream_rq_completed)                                                                 \
  COUNTER  (upstream_rq_pending_total)                                                             \
  COUNTER  (upstream_rq_pending_overflow)                                                          \
  COUNTER  (upstream_rq_pending_failure_eject)                                                     \
  GAUGE    (upstream_rq_pending_active)                                                            \
  COUNTER  (upstream_rq_cancelled)                                                                 \
  COUNTER  (upstream_rq_maintenance_mode)                                                          \
  COUNTER  (upstream_rq_timeout)                                                                   \
  COUNTER  (upstream_rq_per_try_timeout)                                                           \
  COUNTER  (upstream_rq_rx_reset)                                                                  \
  COUNTER  (upstream_rq_tx_reset)                                                                  \
  COUNTER  (upstream_rq_retry)                                                                     \
  COUNTER  (upstream_rq_retry_success)                                                             \
  COUNTER  (upstream_rq_retry_overflow)                                                            \
  COUNTER  (upstream_flow_control_paused_reading_total)                                            \
  COUNTER  (upstream_flow_control_resumed_reading_total)                                           \
  COUNTER  (upstream_flow_control_backed_up_total)                                                 \
  COUNTER  (upstream_flow_control_drained_total)                                                   \
  COUNTER  (bind_errors)                                                                           \
  GAUGE    (max_host_weight)                                                                       \
  COUNTER  (membership_change)                                                                     \
  GAUGE    (membership_healthy)                                                                    \
  GAUGE    (membership_total)                                                                      \
  COUNTER  (retry_or_shadow_abandoned)                                                             \
  COUNTER  (update_attempt)                                                                        \
  COUNTER  (update_success)                                                                        \
  COUNTER  (update_failure)                                                                        \
  COUNTER  (update_empty)                                                                          \
  COUNTER  (update_no_rebuild)                                                                     \
  GAUGE    (version)
// clang-format on

/**
 * All cluster load report stats. These are only use for EDS load reporting and not sent to the
 * stats sink. See envoy.api.v2.endpoint.ClusterStats for the definition of upstream_rq_dropped.
 * These are latched by LoadStatsReporter, independent of the normal stats sink flushing.
 */
// clang-format off
#define ALL_CLUSTER_LOAD_REPORT_STATS(COUNTER)                                                     \
  COUNTER (upstream_rq_dropped)
// clang-format on

/**
 * Cluster circuit breakers stats.
 */
// clang-format off
#define ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(GAUGE)                                                  \
  GAUGE (cx_open)                                                                                  \
  GAUGE (rq_pending_open)                                                                          \
  GAUGE (rq_open)                                                                                  \
  GAUGE (rq_retry_open)
// clang-format on

/**
 * Struct definition for all cluster stats. @see stats_macros.h
 */
struct ClusterStats {
  ALL_CLUSTER_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT, GENERATE_HISTOGRAM_STRUCT)
};

/**
 * Struct definition for all cluster load report stats. @see stats_macros.h
 */
struct ClusterLoadReportStats {
  ALL_CLUSTER_LOAD_REPORT_STATS(GENERATE_COUNTER_STRUCT)
};

/**
 * Struct definition for cluster circuit breakers stats. @see stats_macros.h
 */
struct ClusterCircuitBreakersStats {
  ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(GENERATE_GAUGE_STRUCT)
};

/**
 * All extension protocol specific options returned by the method at
 *   NamedNetworkFilterConfigFactory::createProtocolOptions
 * must be derived from this class.
 */
class ProtocolOptionsConfig {
public:
  virtual ~ProtocolOptionsConfig() {}
};
typedef std::shared_ptr<const ProtocolOptionsConfig> ProtocolOptionsConfigConstSharedPtr;

/**
 *  Base class for all cluster typed metadata factory.
 */
class ClusterTypedMetadataFactory : public Envoy::Config::TypedMetadataFactory {};

/**
 * Information about a given upstream cluster.
 */
class ClusterInfo {
public:
  struct Features {
    // Whether the upstream supports HTTP2. This is used when creating connection pools.
    static const uint64_t HTTP2 = 0x1;
    // Use the downstream protocol (HTTP1.1, HTTP2) for upstream connections as well, if available.
    // This is used when creating connection pools.
    static const uint64_t USE_DOWNSTREAM_PROTOCOL = 0x2;
    // Whether connections should be immediately closed upon health failure.
    static const uint64_t CLOSE_CONNECTIONS_ON_HOST_HEALTH_FAILURE = 0x4;
  };

  virtual ~ClusterInfo() {}

  /**
   * @return bool whether the cluster was added via API (if false the cluster was present in the
   *         initial configuration and cannot be removed or updated).
   */
  virtual bool addedViaApi() const PURE;

  /**
   * @return the connect timeout for upstream hosts that belong to this cluster.
   */
  virtual std::chrono::milliseconds connectTimeout() const PURE;

  /**
   * @return the idle timeout for upstream connection pool connections.
   */
  virtual const absl::optional<std::chrono::milliseconds> idleTimeout() const PURE;

  /**
   * @return soft limit on size of the cluster's connections read and write buffers.
   */
  virtual uint32_t perConnectionBufferLimitBytes() const PURE;

  /**
   * @return uint64_t features supported by the cluster. @see Features.
   */
  virtual uint64_t features() const PURE;

  /**
   * @return const Http::Http2Settings& for HTTP/2 connections created on behalf of this cluster.
   *         @see Http::Http2Settings.
   */
  virtual const Http::Http2Settings& http2Settings() const PURE;

  /**
   * @param name std::string containing the well-known name of the extension for which protocol
   *        options are desired
   * @return std::shared_ptr<const Derived> where Derived is a subclass of ProtocolOptionsConfig
   *         and contains extension-specific protocol options for upstream connections.
   */
  template <class Derived>
  const std::shared_ptr<const Derived>
  extensionProtocolOptionsTyped(const std::string& name) const {
    return std::dynamic_pointer_cast<const Derived>(extensionProtocolOptions(name));
  }

  /**
   * @return const envoy::api::v2::Cluster::CommonLbConfig& the common configuration for all
   *         load balancers for this cluster.
   */
  virtual const envoy::api::v2::Cluster::CommonLbConfig& lbConfig() const PURE;

  /**
   * @return the type of load balancing that the cluster should use.
   */
  virtual LoadBalancerType lbType() const PURE;

  /**
   * @return the service discovery type to use for resolving the cluster.
   */
  virtual envoy::api::v2::Cluster::DiscoveryType type() const PURE;

  /**
   * @return configuration for least request load balancing, only used if LB type is least request.
   */
  virtual const absl::optional<envoy::api::v2::Cluster::LeastRequestLbConfig>&
  lbLeastRequestConfig() const PURE;

  /**
   * @return configuration for ring hash load balancing, only used if type is set to ring_hash_lb.
   */
  virtual const absl::optional<envoy::api::v2::Cluster::RingHashLbConfig>&
  lbRingHashConfig() const PURE;

  /**
   * @return const absl::optional<envoy::api::v2::Cluster::OriginalDstLbConfig>& the configuration
   *         for the Original Destination load balancing policy, only used if type is set to
   *         ORIGINAL_DST_LB.
   */
  virtual const absl::optional<envoy::api::v2::Cluster::OriginalDstLbConfig>&
  lbOriginalDstConfig() const PURE;

  /**
   * @return Whether the cluster is currently in maintenance mode and should not be routed to.
   *         Different filters may handle this situation in different ways. The implementation
   *         of this routine is typically based on randomness and may not return the same answer
   *         on each call.
   */
  virtual bool maintenanceMode() const PURE;

  /**
   * @return uint64_t the maximum number of outbound requests that a connection pool will make on
   *         each upstream connection. This can be used to increase spread if the backends cannot
   *         tolerate imbalance. 0 indicates no maximum.
   */
  virtual uint64_t maxRequestsPerConnection() const PURE;

  /**
   * @return the human readable name of the cluster.
   */
  virtual const std::string& name() const PURE;

  /**
   * @return ResourceManager& the resource manager to use by proxy agents for this cluster (at
   *         a particular priority).
   */
  virtual ResourceManager& resourceManager(ResourcePriority priority) const PURE;

  /**
   * @return Network::TransportSocketFactory& the factory of transport socket to use when
   *         communicating with the cluster.
   */
  virtual Network::TransportSocketFactory& transportSocketFactory() const PURE;

  /**
   * @return ClusterStats& strongly named stats for this cluster.
   */
  virtual ClusterStats& stats() const PURE;

  /**
   * @return the stats scope that contains all cluster stats. This can be used to produce dynamic
   *         stats that will be freed when the cluster is removed.
   */
  virtual Stats::Scope& statsScope() const PURE;

  /**
   * @return ClusterLoadReportStats& strongly named load report stats for this cluster.
   */
  virtual ClusterLoadReportStats& loadReportStats() const PURE;

  /**
   * Returns an optional source address for upstream connections to bind to.
   *
   * @return a source address to bind to or nullptr if no bind need occur.
   */
  virtual const Network::Address::InstanceConstSharedPtr& sourceAddress() const PURE;

  /**
   * @return the configuration for load balancer subsets.
   */
  virtual const LoadBalancerSubsetInfo& lbSubsetInfo() const PURE;

  /**
   * @return const envoy::api::v2::core::Metadata& the configuration metadata for this cluster.
   */
  virtual const envoy::api::v2::core::Metadata& metadata() const PURE;

  /**
   * @return const Envoy::Config::TypedMetadata&& the typed metadata for this cluster.
   */
  virtual const Envoy::Config::TypedMetadata& typedMetadata() const PURE;

  /**
   *
   * @return const Network::ConnectionSocket::OptionsSharedPtr& socket options for all
   *         connections for this cluster.
   */
  virtual const Network::ConnectionSocket::OptionsSharedPtr& clusterSocketOptions() const PURE;

  /**
   * @return whether to skip waiting for health checking before draining connections
   *         after a host is removed from service discovery.
   */
  virtual bool drainConnectionsOnHostRemoval() const PURE;

protected:
  /**
   * Invoked by extensionProtocolOptionsTyped.
   * @param name std::string containing the well-known name of the extension for which protocol
   *        options are desired
   * @return ProtocolOptionsConfigConstSharedPtr with extension-specific protocol options for
   *         upstream connections.
   */
  virtual ProtocolOptionsConfigConstSharedPtr
  extensionProtocolOptions(const std::string& name) const PURE;
};

typedef std::shared_ptr<const ClusterInfo> ClusterInfoConstSharedPtr;

class HealthChecker;

/**
 * An upstream cluster (group of hosts). This class is the "primary" singleton cluster used amongst
 * all forwarding threads/workers. Individual HostSets are used on the workers themselves.
 */
class Cluster {
public:
  virtual ~Cluster() {}

  enum class InitializePhase { Primary, Secondary };

  /**
   * @return a pointer to the cluster's health checker. If a health checker has not been installed,
   *         returns nullptr.
   */
  virtual HealthChecker* healthChecker() PURE;

  /**
   * @return the information about this upstream cluster.
   */
  virtual ClusterInfoConstSharedPtr info() const PURE;

  /**
   * @return a pointer to the cluster's outlier detector. If an outlier detector has not been
   *         installed, returns nullptr.
   */
  virtual Outlier::Detector* outlierDetector() PURE;
  virtual const Outlier::Detector* outlierDetector() const PURE;

  /**
   * Initialize the cluster. This will be called either immediately at creation or after all primary
   * clusters have been initialized (determined via initializePhase()).
   * @param callback supplies a callback that will be invoked after the cluster has undergone first
   *        time initialization. E.g., for a dynamic DNS cluster the initialize callback will be
   *        called when initial DNS resolution is complete.
   */
  virtual void initialize(std::function<void()> callback) PURE;

  /**
   * @return the phase in which the cluster is initialized at boot. This mechanism is used such that
   *         clusters that depend on other clusters can correctly initialize. (E.g., an EDS cluster
   *         that depends on resolution of the EDS server itself).
   */
  virtual InitializePhase initializePhase() const PURE;

  /**
   * @return the PrioritySet for the cluster.
   */
  virtual PrioritySet& prioritySet() PURE;

  /**
   * @return the const PrioritySet for the cluster.
   */
  virtual const PrioritySet& prioritySet() const PURE;
};

typedef std::shared_ptr<Cluster> ClusterSharedPtr;

} // namespace Upstream
} // namespace Envoy
